package in.muton.edushare.pages.cp.backup;

import in.muton.edushare.EduShareSession;
import in.muton.edushare.pages.EduShareBasePage;
import in.muton.edushare.pages.common.EdusharePanel;
import in.muton.edushare.wicket.models.CayenneModel;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.EmptyPanel;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.time.Duration;

import com.edushare.common.backup.BackupProcessThread;
import com.edushare.common.backup.SystemBackupUtils;
import com.edushare.common.dao.SystemBackupDAO;
import com.edushare.common.exceptions.EduShareRuntimeException;
import com.edushare.common.exceptions.ProcessingException;
import com.edushare.common.util.DateTimeUtil;
import com.edushare.common.vo.ConfigProperties;
import com.edushare.common.vo.SystemBackup;
import com.edushare.common.vo.Universities;

/**
 * Based on whether a backup process is running, it shows start-backup or processing status panel.
 * @author naishe
 *
 */
public class CreateBackupPanel extends EdusharePanel {

	private static final long serialVersionUID = -2719227934080835251L;
	private Log log = LogFactory.getLog(CreateBackupPanel.class);
	private Fragment frag;
	private AjaxSelfUpdatingTimerBehavior updateBehaviour;
	private IModel<Universities> univModel;
	
	public CreateBackupPanel(String id, IModel<Universities> model, EduShareBasePage page) {
		super(id, model, page);
		this.univModel = model;
		SystemBackup bkp = new SystemBackupDAO(getORMInterface()).getCurrentlyRunningBackup();
		if(bkp==null){
			add(frag = new CreateBackupFragment("stateFragment", this, model));
//			add();
		}else{
			loadStatusFragment();
		}
	}
	
	/**
	 * This fragment gives option to start a backup process.
	 * Note: this fragment shows only when No Backup processor is running.
	 * @author naishe
	 *
	 */
	public class CreateBackupFragment extends Fragment implements IHeaderContributor{

		private static final long serialVersionUID = -4951195937143358985L;
		private WebMarkupContainer alertBeforeStart;
		private Form<Void> f;
		
		public CreateBackupFragment(String id, MarkupContainer markupProvider, IModel<Universities> model) {
			super(id, "createNew", markupProvider, model);
			add(f = new Form<Void>("startBkp"){
				
				private static final long serialVersionUID = 1867250152988895349L;

				@Override
				protected void onSubmit() {
					SystemBackup bkp = new SystemBackup();
					bkp.setBackupTitle("Backup @ "+DateTimeUtil.formatTimestamp(new Date()));
					bkp.setBackupType(SystemBackup.BACKUP_TYPE_COMPLETE);
					bkp.setCreator(getLoggedInUser());
					bkp.setUniversity(getLoggedInUser().getRoleList().get(0).getUniversity());
					bkp.setBackupStatus(ConfigProperties.SYSTEM_BACKUP_STATUS_PROCESSING_INIT);
					new SystemBackupDAO(EduShareSession.get().getSessionORMInterface()).createBackup(bkp);
					EduShareSession.get().getSessionORMInterface().commitAllChanges();
					try {
						BackupProcessThread thread = new BackupProcessThread(bkp);
						new Thread(thread).start();
					} catch (ProcessingException e) {
						e.printStackTrace();
						throw new EduShareRuntimeException("thread broke");
					}
					
					loadStatusFragment();
				}
			});
			f.add(alertBeforeStart = new WebMarkupContainer("alertBeforeStart"));
			alertBeforeStart.setOutputMarkupId(true);
			
			SystemBackup latestBkp = new SystemBackupDAO(getORMInterface()).getLatestBackup(ConfigProperties.SYSTEM_BACKUP_STATUS_OK);
			if(latestBkp!=null)
				add(new FilesCreatedFrag("lastbkpDiv", CreateBackupPanel.this, new CayenneModel<SystemBackup>(latestBkp)));
			else
				add(new EmptyPanel("lastbkpDiv"));
		}

		public void renderHead(IHeaderResponse resp) {
			resp.renderOnDomReadyJavascript("" +
					"$('#"+alertBeforeStart.getMarkupId()+"').dialog({"+
				    "autoOpen: false,"+
				    "width: 600,"+
				    "modal: true,"+
				    "buttons: {"+
				        "\"Ok\": function() {"+ 
				        "$(this).dialog(\"close\");"+
				        "$('#"+f.getMarkupId()+"').submit();"+
				         "return true; "+
				        "}, "+
				        "\"Cancel\": function() {"+ 
					    "$(this).dialog(\"close\");"+ 
				            "return false; "+
				        "} "+
				    "}"+
				"});"+
				"$('#startBkp').click(function(){$('#"+alertBeforeStart.getMarkupId()+"').dialog('open');return false;});"+
					"");
		}
		
	}
	
	private static int reloads = 0;
	/**
	 * <p>
	 * Class for displaying step by step progress of a process.
	 * This is displayed (1) Just after someone clicks "start back-up" in CreateBackupFragment class
	 * or (2) Someone accesses create backup page when a process is already running.
	 * </p>
	 * @author naishe
	 *
	 */
	public class ProcessingStatusFragment extends Fragment{

		private static final long serialVersionUID = -2606061366817872506L;
		private Label status;
		private IModel<SystemBackup> bkpModel;
		private Component procList;
		private WebMarkupContainer bkpProcessing;
		private WebMarkupContainer bkpSuccessful;
		
		public ProcessingStatusFragment(String id, MarkupContainer markupProvider) {
			super(id, "bkpProcessingFrag", markupProvider);
			SystemBackup bkp = new SystemBackupDAO(getORMInterface()).getCurrentlyRunningBackup();
			if(bkp==null){
				log.debug("--------------- NO RUNNIG PROCESS --------------");
				//throw error here!
			}
			log.debug("---------------- RUNNNIG " + bkp.getBackupTitle()+ " ----------");
			bkpModel = new CayenneModel<SystemBackup>(bkp);
			add(status = new Label("tmpStts", reloads +" :"+bkp.getCurrentProcess()));
			add(procList=new WebMarkupContainer("procList"));
			add(bkpProcessing = new WebMarkupContainer("bkpProcessing"));
			add(bkpSuccessful = new WebMarkupContainer("bkpSuccessful"));
			bkpSuccessful.setVisible(false);
			Link<Void> filelnk;
			bkpSuccessful.add(filelnk=new Link<Void>("flBkpLnk"){

				private static final long serialVersionUID = 3235458421586815541L;

				@Override
				public void onClick() {
					Fragment fileFrag;
					frag.replaceWith(fileFrag = new CreateBackupFragment("stateFragment", CreateBackupPanel.this, univModel));
					frag = fileFrag;
				}
				
			});
			filelnk.add(new Label("flBkpName", bkpModel.getObject().getBackupTitle()));
		}
		
		@Override
		protected void onBeforeRender() {
			reloads++;
			Label sts = new Label("tmpStts", reloads +" :"+ bkpModel.getObject().getBackupStatus());
			status.replaceWith(sts);
			status = sts;
			ArrayList<Integer> processList = SystemBackupUtils.getProcessSequence(bkpModel.getObject());
			final HashMap<Integer, Integer> processStatusMap = bkpModel.getObject().getCurrentProcessingStatus();
				
			ListView<Integer> newlv;
			procList.replaceWith(newlv = new ListView<Integer>("procList", processList){

				private static final long serialVersionUID = 9190861553399919239L;

				@Override
				protected void populateItem(ListItem<Integer> item) {
					item.add(new Label("procName", ConfigProperties.getStringValueForStatus(item.getModelObject())));
					WebMarkupContainer wmc;
					item.add(wmc = new WebMarkupContainer("iconSpan"));
					if(processStatusMap.get(item.getModelObject()) == SystemBackup.PROCESS_FINISHED_SUCCESSFULLY)
						wmc.add(new AttributeAppender("class", true, new Model<String>("ui-icon-check"), " "));
					else if(processStatusMap.get(item.getModelObject()) == SystemBackup.PROCESS_PROCESSING)
						wmc.add(new AttributeAppender("class", true, new Model<String>("ui-icon-play"), " "));
					else
						wmc.add(new AttributeAppender("class", true, new Model<String>("ui-icon-pause"), " "));
					
					if(bkpModel.getObject().getBackupStatus() == ConfigProperties.SYSTEM_BACKUP_STATUS_OK)
					{
						bkpProcessing.setVisible(false);
						bkpSuccessful.setVisible(true);
					}
				}
				
			});
			
			procList = newlv;
			
			if(bkpModel.getObject().getBackupStatus() == ConfigProperties.SYSTEM_BACKUP_STATUS_OK)
				updateBehaviour.stop();
			super.onBeforeRender();
			
		}
		
	}
	
	/**
	 * Class for showing files associated with a particular system backup. It renders immediate after processing is over.
	 * @author naishe
	 *
	 */
	public class FilesCreatedFrag extends Fragment{

		private static final long serialVersionUID = 9075944485134898968L;

		public FilesCreatedFrag(String id, MarkupContainer markupProvider, IModel<SystemBackup> model) {
			super(id, "bkpFiles", markupProvider, model);
			add(new Label("creationTimestamp", DateTimeUtil.formatTimestamp(model.getObject().getCreationTimestamp())));
			final HashMap<String, String> fileUrlMap = model.getObject().getBackupFileURLsMap();
			ArrayList<String> fnList = new ArrayList<String>(fileUrlMap.keySet());
			add(new ListView<String>("bkpFiles", fnList){

				private static final long serialVersionUID = 9143792772828457235L;

				@Override
				protected void populateItem(ListItem<String> item) {
					item.add(new ExternalLink("fileName", fileUrlMap.get(item.getModelObject()), item.getModelObject()));
					item.add(new ExternalLink("dldLink", fileUrlMap.get(item.getModelObject())));
				}
				
			});
		}
		
	}
	
	/** This method creates Status fragment and attaches self updating timer to it */
	private void loadStatusFragment(){
		Fragment stateFrag = new ProcessingStatusFragment("stateFragment", CreateBackupPanel.this);
		if(frag!=null)//if available replace
			frag.replaceWith(stateFrag);
		else //else add
			add(stateFrag);
		
		frag = stateFrag; //put the status frag as current frag, so that we can replace it.
		//Add selfUpdating behaviour 
		stateFrag.add(updateBehaviour = new AjaxSelfUpdatingTimerBehavior(Duration.milliseconds(1000)));
	}

}
